; CoBra CP/M Game Loader
; 
; CPU Type: Z80
; 
; Created for Linux z80-asm
; 
; on 2017-02-09 09:17
;
;				; This 512 byte code (loader) is used for loading
				; a Spectrum Basic application from CP/M.
				; The application is usually cracked by
				; using a NMI button & NMI routine that saves
				; the upper 48K of Spectrum memory with the
				; "snapshot" of the application at that moment
				; Then the 48K of code is copied to a CP/M disk,
				; the code sequence below is attached in front
				; of it and the result is saved as a .COM file
				; on disk. When the executable .COM obtained this way
				; is run under CP/M, it is loaded into memory
				; starting at 0100h and then run from this
				; address. So the sequence below is always
				; run first. What it does is to move the actual
				; application code (48K) from 0300-C2FF to
				; 4000-FFFF, copy the Spectrum screen portion
				; from the application snapshot into the video
				; memory of CP/M (the game start screen usually),
				; set the stack pointer and load the entry point
				; of the application "snapshot" into the HL register
				; and then change the hardware configuration to BASIC.
				; After changing to BASIC config, execution will start
				; at the entry point stored in HL. This is the entry point
				; of the CPU RESTORE routine which must be inserted
				; somewhere in the block of code saved by NMI, preferrably
				; above the video memory so the screen doesn't get corrupted.
				; The entry point must be chosen after examining the code
				; to find an unused area in it, big enough to contain the
				; CPU RESTORE routine. The entry point is at the same time
				; the temporary stack base while CPU RESTORE is running.
	ORG	$0100
	JR	START
CPURES:	DEFW	$0000		; BASIC ENTRY POINT (to CPU RESTORE) & TEMPORARY STACK POINTER (while executing CPU RESTORE)
				; ########### ADJUST THIS VALUE TO ANY FREE ZONE WITHIN CODE ABOVE VIDEO MEM ###########
				; ########### WHERE THE CPU RESTORE ROUTINE CAN BE INSERTED		     ###########
START:	DI
	LD	HL,$C1FF	; > move 48K app code
	LD	DE,$FFFF	; > from 0200-C1FF (this Game Loader takes up $100 bytes)
	LD	BC,$C000	; > to 4000-FFFF
	LDDR			; > (to std. Spectrum RAM area)
	LD	BC,$1B00	; Spectrum screen mem size
	LD	HL,$4000	; app screen data pointer
LOOP1:	LD	D,(HL)		; <--- <--- <---| read 1 byte of app screen data (from DRAM#1)
	LD	A,$40		;		|
	OUT	($FE),A		; set O6 to 1	| (CPU access to VRAM in CP/M)
	LD	(HL),D		;		| write data to CP/M screen mem
	XOR	A		;		|
	OUT	($FE),A		; set O6 to 0	| (CPU access to DRAM#1 in CP/M)
	INC	HL		;		| increment screen data pointer
	DEC	BC		;		| decrement Spectrum screen size counter
	LD	A,B		; test if	|
	OR	C		; BC=00		|
	JR	NZ,LOOP1	; ---> ---> --->| restart loop if Spectrum screen size counter > 0
	
	LD	SP,(CPURES)	; set SP = entry point of CPU RESTORE
	LD	HL,(CPURES)	; set HL = entry point of CPU RESTORE
	LD	A,$03		;
	OUT	($E3),A		; disable
	OUT	($EB),A		; interrupts
	OUT	($F3),A		; from
	OUT	($FB),A		; Z80-CTC
	XOR	A		;
	OUT	($FD),A		; send invalid command to 8272 (set to standby)
LOOP2:	LD	A,$00		; <--- <--- <--- <--- <--- <----|
	IN      A, ($FE)	; read keyboard			|
	AND	$3F		;				|
	CP	$3F		; test if any key pressed	|
	JR	NZ,SKIP		; exit loop if key pressed	|
	JR	LOOP2		; ----> ---> ---> ---> ---> --->|
SKIP:	EXX			; preserve HL = app entry point
	LD	HL,$0038	; start addr of BASIC loading routine in BOOT ROM
	LD	A,$C1		; set SO to 1, O6 to 1, border to blue
	OUT	($FE),A		; (SO selects BASIC in a 32K ROM, O6=1 for startup hw config)
	LD	R,A		; set bit 7 of R to 1 for startup hw config
	JP	(HL)		; after changing to startup hw config, jump to 0038h